using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Data.Measurement;
using FilteredStatistics.Common;
using FilteredStatistics.Common.Controller;
using FilteredStatistics.Controller;
using FilteredStatistics.View;

namespace FilteredStatistics.Data
{
    class TemplateCustomTimeLapsFilterCriteria : FilterCriteriaFacade, ISplitsCriteria
    {
        public TemplateCustomTimeLapsFilterCriteria(TimeSpan filterDuration, TimeSpan lapDuration)
        {
            m_ActivityCallbackHelper = new ActivityDataChangedHelper(null);
            m_ActivityCallbackHelper.PropertyChanged += new PropertyChangedEventHandler(OnActivityDataChanged);

            Init(filterDuration, lapDuration);
        }

#region FilterCriteriaFacade members

        public override Guid ReferenceId
        {
            get { return GUIDs.TemplateSplitsCriteriaGuid; }
        }
        
        public override IActivity Activity
        {
            set
            {
                m_Activity = value;
                m_ActivityCallbackHelper.Activity = m_Activity;

                foreach (TimeLapNamedZone zone in ConcreteNamedZones)
                {
                    zone.Activity = value;
                }
            }
        }

        public override String DisplayName
        {
            get { return PluginView.GetLocalizedString("CustomDistanceLapsText"); }
        }

        public override List<object> NamedZones
        {
            get { return m_SplitsNamedZones; }
        }

        public override bool IsTemplateOnly
        {
            get { return true; }
        }

        public override bool IsMainViewOnly
        {
            get { return false; }
        }

        public override bool IsSerializable
        {
            get { return true; }
        }

        public override UInt16 DataVersion
        {
            get { return Constants.CurrentVersion.VersionNumber; }
        }

        public override void SerializeCriteria(Stream stream)
        {
            Serialize(stream);
        }

        public override object DeserializeCriteria(Stream stream, UInt16 version)
        {
            Deserialize(stream, new DataVersion(version));

            return this;
        }

        public override object TemplateCompatibleCriteria
        {
            get
            {
                return new TemplateCustomTimeLapsFilterCriteria(m_FilterDuration, m_LapDuration);
            }
        }

#endregion

#region ISplitsCriteria Members

        public SplitsCriteriaType SplitsType
        {
            get { return SplitsCriteriaType.CustomTime; }
        }

#endregion

#region IPluginSerializable members

        public override void Serialize(Stream stream)
        {
            base.Serialize(stream);

            SerializableInt32Range splitsType = new SerializableInt32Range((Int32)SplitsCriteriaType.CustomTime);
            SerializableTimeSpan totalDuration = new SerializableTimeSpan(m_FilterDuration);
            SerializableTimeSpan duration = new SerializableTimeSpan(m_LapDuration);

            splitsType.Serialize(stream);
            totalDuration.Serialize(stream);
            duration.Serialize(stream);
       }

#endregion

#region Deserialization methods

        public void Deserialize_V1(Stream stream, DataVersion version)
        {
            SerializableTimeSpan totalDuration = new SerializableTimeSpan(m_FilterDuration);
            SerializableTimeSpan duration = new SerializableTimeSpan(m_LapDuration);

            totalDuration.Deserialize(stream, version);
            duration.Deserialize(stream, version);

            Init(totalDuration, duration);
        }

#endregion

        private void OnActivityDataChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Activity.Duration")
            {
                CreateSplitsNamedZonesList();
            }
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            TemplateCustomTimeLapsFilterCriteria criteria = obj as TemplateCustomTimeLapsFilterCriteria;
            SplitsPlaceholderFilterCriteria criteriaPlaceholder = obj as SplitsPlaceholderFilterCriteria;

            if (criteriaPlaceholder != null)
            {
                return true;
            }
            else if (criteria != null)
            {
                return criteria.LapTime == LapTime && criteria.MaximumTime == MaximumTime;
            }

            return base.Equals(obj);
        }

        private void CreateSplitsNamedZonesList()
        {
            TimeSpan lapStartTime = new TimeSpan(0, 0, 0);
            TimeSpan lapEndTime = lapStartTime + m_LapDuration;
            int lapIndex = 0;

            // Create/update a named zone for each distance interval
            while (lapStartTime < m_FilterDuration)
            {
                TimeLapNamedZone currentNamedZone;

                if(m_SplitsNamedZones.Count > lapIndex)
                {
                    // Reuse the already created named zone
                    currentNamedZone = m_SplitsNamedZones[lapIndex] as TimeLapNamedZone;
                    currentNamedZone.Init(m_Activity, lapStartTime, new TimeSpan(0, 0, (int)Math.Min(m_FilterDuration.TotalSeconds, lapEndTime.TotalSeconds)));
                }
                else
                {
                    // Create a new one
                    currentNamedZone = new TimeLapNamedZone(m_Activity, lapIndex, lapStartTime, new TimeSpan(0, 0, (int)Math.Min(m_FilterDuration.TotalSeconds, lapEndTime.TotalSeconds)));
                    m_SplitsNamedZones.Add(currentNamedZone);
                }

                lapStartTime = lapEndTime;
                lapEndTime += m_LapDuration;
                lapIndex++;
            }

            // Remove excess named zones if required
            if (lapIndex < m_SplitsNamedZones.Count)
            {
                m_SplitsNamedZones.RemoveRange(lapIndex, m_SplitsNamedZones.Count - lapIndex);
            }

            TriggerNamedZonesListChanged();
        }

        public void Init(TimeSpan filterDuration, TimeSpan duration)
        {
            m_LapDuration = duration;
            m_FilterDuration = filterDuration;

            CreateSplitsNamedZonesList();
        }

        public TimeSpan LapTime
        {
            get { return m_LapDuration; }
        }

        public TimeSpan MaximumTime
        {
            get { return m_FilterDuration; }
        }

        private IActivity m_Activity = null;
        private ActivityDataChangedHelper m_ActivityCallbackHelper = null;
        private TimeSpan m_FilterDuration;
        private TimeSpan m_LapDuration;
        private List<object> m_SplitsNamedZones = new List<object>();
    }
}
